#include <sstream>
#include <fstream>
#include <iostream>

#include "emailsrv.h"
#include "../Headers/log.h"
#include "../Headers/types.h"
#include "../Headers/authr.h"
#include "../Headers/config.h"
#include "../AR/authorization.h"
#include "../USS/USSclient.h"

// Constructor
TOB::EmailSRV::EmailSRV(TOB::Nameserver::IP nsip, std::string username, std::string password): 
  AuthRequired(nsip, username, password) { 
  std::string addr;
  // Read config
  Config config(CONFIG);
  mailPath = config["mail-path"];
  if(!mailPath.size()) { mailPath = MAILPATH; }
  if(mailPath[mailPath.size()-1] != '/') { mailPath += '/'; }
  defMailboxSize = (uint32)config.intValue("mailbox-size");
  if(!defMailboxSize) { defMailboxSize = MAILBOXSIZE; }

  // Get authorization server
  Nameserver::convertIP(nsip, addr);
  Nameserver ns(addr);
  Nameserver::IP arip;
  if((result = ns.requestAddress(Server::Authorization, arip)) != Code::Okay) {
    Log::error("EM: Can't resolve ar server");
    return;
  }

  // Check
  Authorization ar(arip, username, password);
  result = ar.Email(allowed);
}


// Destructor
TOB::EmailSRV::~EmailSRV() { }


// Send letter
Code::Type TOB::EmailSRV::sendLetter(std::string header, std::string body) {
  if(result != Code::Okay) { 
    Log::error("EM: Forbidden");
    return result; 
  }
  if(!allowed) { return Code::EM_Forbidden; }

  // Parse header
  std::stringstream stream(header);
  getline(stream, recepient);
  getline(stream, subject);

  // Check login
  US us(nsip);
  int value;
  if((result = us.existenceLogin(recepient, value)) != Code::Okay) { return result; }
  if(value) { return Code::EM_UserNotFound; }

  // Count mail
  if(!readUserInfo()) { return Code::EM_CantPerformOperation; }
  std::ifstream in((mailPath + recepient).c_str());
  if(in.is_open()) {
    std::string line;
    uint32 count = 0;
    while(in.good()) {
      getline(in, line);
      ++count;
    }
    in.close();
    if(count >= mailboxSize) { return Code::EM_MailboxIsFull; }
  }

  // Send letter
  std::stringstream letterSS;
  letterSS << mailPath << recepient << '.' << lastId;
  std::string letterPath = letterSS.str();

  // Table entry
  std::fstream out((mailPath + recepient).c_str(), std::ios::out | std::ios::app);
  if(!out.is_open()) { return Code::EM_CantPerformOperation; }
  out << username << ' ' << lastId << ' ' << subject << std::endl;
  out.close();

  // Mail body
  std::ofstream lout(letterPath.c_str());
  if(!lout.is_open()) { return Code::EM_CantPerformOperation; }
  lout << body;
  lout.close();

  // Write info
  ++lastId;
  if(!writeUserInfo()) { return Code::EM_CantPerformOperation; }
  return Code::Okay;
}


// Read letter
Code::Type TOB::EmailSRV::readLetter(uint32 letterId, std::string &body) {
  if(result != Code::Okay) { return result; }
  if(!allowed) { return Code::EM_Forbidden; }

  // Open table
  std::stringstream stream;
  std::ifstream in((mailPath + username).c_str());
  if(in.is_open()) {
    uint32 count = 0;
    std::string line, tmp;

    // Read table
    while(in.good()) {
      getline(in, line);
      if(line.size() && count == letterId) {
        // Get letter
        std::stringstream entry(line);
        stream << mailPath;
        stream << username;
        entry >> tmp;
        entry >> tmp;
        stream << '.' << tmp;
        Log::event("Reading '" + stream.str() + "'");

        // Read letter
        const char *p = stream.str().c_str();
        std::ifstream lin(p);
        if(!lin.is_open()) { return Code::EM_CantPerformOperation; }
        lin.seekg(0, std::ios::end);
        uint32 length = lin.tellg();
        lin.seekg(0, std::ios::beg);
        char *buffer = new char[length+1];
        buffer[length] = 0;
        lin.read(buffer, length);
        body = std::string(buffer);
        delete[] buffer;

        return Code::Okay;
      }
      ++count;
    }
  }

  return Code::EM_End;
}


// Expand mailbox
Code::Type TOB::EmailSRV::expandMail() {
  if(result != Code::Okay) { return result; }
  if(!allowed) { return Code::EM_Forbidden; }

  recepient = username;
  readUserInfo();
  mailboxSize += defMailboxSize;
  writeUserInfo();

  return Code::Okay;
}


// Get next subject
Code::Type TOB::EmailSRV::getNextSubject(uint32 letterId, std::string &header) {
  if(result != Code::Okay) { return result; }
  if(!allowed) { return Code::EM_Forbidden; }

  // Open table
  std::stringstream stream;
  std::ifstream in((mailPath + username).c_str());
  if(in.is_open()) {
    uint32 count = 0;
    std::string line, tmp;

    // Read line by line
    while(in.good()) {
      getline(in, line);
      if(line.size() && count == letterId) {
        // Assembly header
        std::stringstream entry(line);
        entry >> tmp;
        stream << tmp << std::endl;
        entry >> tmp;
        getline(entry, tmp);
        stream << tmp.substr(1);
        // "Send"
        header = stream.str();
        return Code::Okay;
      }
      ++count;
    }
  }

  return Code::EM_End;
}


// Read .info
bool TOB::EmailSRV::readUserInfo() {
  std::ifstream stream((mailPath + recepient + MAILSUFFIX).c_str());
  if(!stream.is_open()) { 
    lastId = 0; 
    mailboxSize = defMailboxSize;
    return writeUserInfo();
  }
  stream.read((char*)&lastId, sizeof(uint32));
  stream.read((char*)&mailboxSize, sizeof(uint32));
  stream.close();
  return true;
}


// Write .info
bool TOB::EmailSRV::writeUserInfo() {
  std::ofstream stream((mailPath + recepient + MAILSUFFIX).c_str());
  if(!stream.is_open()) { return false; }
  stream.write((char*)&lastId, sizeof(uint32));
  stream.write((char*)&mailboxSize, sizeof(uint32));
  stream.close();
  return true;
}
